/**
 * Copyright © 2014 TingerSure. All rights reserved.
 */
package org.sure.scomet.util.log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.sure.scomet.util.ConfigManager;

/**
 * 
 * This is a logging tool class, which provides the framework for all of the
 * logs related methods.
 * 
 * @author TingerSure
 * 
 */
public class LogManager {

	/**
	 * the date format string of the date in the log file
	 */
	private static String dateFormat = null;

	/**
	 * the date format tool
	 */
	private static DateFormat format = null;

	/**
	 * the character set of the log file
	 */
	private static String logCharacter = null;

	/**
	 * the flag which sign out if log information is needed to print on the
	 * console
	 */
	private static boolean logConsole = false;

	/**
	 * the flag which sign out that if the log information is needed to write
	 * into the file and print on the console(if need) by log type <br>
	 * 
	 * The next four are alike.
	 * 
	 * @see {@link LogType}
	 */
	private static boolean logDebug = false;
	private static boolean logInfo = false;
	private static boolean logWarn = false;
	private static boolean logError = false;
	private static boolean logFatal = false;
	/**
	 * the log file
	 */
	private static File logFile = null;
	/**
	 * a buffered writer of the log file
	 */
	private static BufferedWriter logWriter = null;

	/**
	 * Destroy all attributes.
	 */
	public static void destroy() {
		logDebug = false;
		logInfo = false;
		logWarn = false;
		logError = false;
		logFatal = false;
		dateFormat = null;
		format = null;
		logConsole = false;
		logCharacter = null;
		if (logWriter != null) {
			try {
				logWriter.flush();
				logWriter.close();
			} catch (IOException e) {
				logAsConsole("Log file can not be closed. " + stackTrace(e),
						LogType.warn);
			}
			logWriter = null;
		}
		logFile = null;
	}

	/**
	 * Initialize all attributes by the configuration file.
	 */
	public static void init() {
		logDebug = "true".equals(ConfigManager.getConfig("log-debug", "false"));
		logInfo = "true".equals(ConfigManager.getConfig("log-info", "false"));
		logWarn = "true".equals(ConfigManager.getConfig("log-warn", "false"));
		logError = "true".equals(ConfigManager.getConfig("log-error", "false"));
		logFatal = "true".equals(ConfigManager.getConfig("log-fatal", "false"));
		logCharacter = ConfigManager.getConfig("log-character", "utf-8");
		dateFormat = ConfigManager.getConfig("log-date-format",
				"yyyy-MM-dd hh:mm:ss:SSS");
		format = new SimpleDateFormat(dateFormat);
		logConsole = "true".equals(ConfigManager.getConfig("log-console",
				"false"));
		String logFileName = ConfigManager.getConfig("log-file", null);
		if (logFileName != null) {
			logFile = new File(logFileName);
			if (!logFile.exists()) {
				try {
					logFile.createNewFile();
				} catch (IOException e) {
					logFile = null;
					logAsConsole(
							"Log file does not exist and can not be created. "
									+ stackTrace(e), LogType.error);
				}
			}
			try {
				logWriter = new BufferedWriter(new OutputStreamWriter(
						new FileOutputStream(logFile, true), logCharacter));
			} catch (UnsupportedEncodingException e) {
				logFile = null;
				logAsConsole("Log character is Unsupported. " + stackTrace(e),
						LogType.error);
			} catch (FileNotFoundException e) {
				logFile = null;
				logAsConsole("Log file is not found. " + stackTrace(e),
						LogType.error);
			}
		} else {
			logFile = null;
		}
	}

	/**
	 * Determine whether a log type that needs to be output.
	 * 
	 * @param type
	 *            the log type
	 * @return need or need not
	 * 
	 * @see {@link LogType}
	 */
	private static boolean inNeed(LogType type) {
		switch (type) {
		case debug:
			return logDebug;
		case info:
			return logInfo;
		case warn:
			return logWarn;
		case error:
			return logError;
		case fatal:
			return logFatal;
		}
		return false;
	}

	/**
	 * Record the given log information as the given log type on log file or on
	 * the console in need.
	 * 
	 * @param info
	 *            the message what will be recorded
	 * @param type
	 *            the type of the log
	 * @see {@link LogType}
	 */
	public static synchronized void log(String info, LogType type) {
		if (inNeed(type)) {
			String message = format.format(new Date()) + "\t" + type + "\t"
					+ info + "\r\n";
			if (logConsole) {
				System.out.print(message);
			}
			if (logFile != null) {
				try {
					logWriter.write(message);
					logWriter.flush();
				} catch (IOException e) {
					logAsConsole("Can not write log to log file:\r\n\t"
							+ message, LogType.error);
				}
			}
		}
	}

	/**
	 * Record the given log information as the given log type on the console.
	 * 
	 * @param info
	 *            the message what will be recorded
	 * @param type
	 *            the type of the log
	 * @see {@link LogType}
	 */
	public static void logAsConsole(String info, LogType type) {
		String message = format.format(new Date()) + "\t" + type + "\t" + info
				+ "\r\n";
		System.out.print(message);
	}

	/**
	 * Record the given log information and exception as the given log type on
	 * log file or on the console in need.
	 * 
	 * @param info
	 *            the message what will be recorded
	 * @param e
	 *            the exception
	 * @param type
	 *            the type of the log
	 * 
	 * @see {@link LogType}
	 */
	public static void logException(String info, Exception e, LogType type) {
		log(info + "  " + stackTrace(e), type);
	}

	/**
	 * Format an exception information as a log message.
	 * 
	 * @param e
	 *            the exception
	 * @return the format string
	 * 
	 * @see {@link Exception}
	 */
	private static String stackTrace(Exception e) {
		StringBuilder stackTraceMessage = new StringBuilder();
		StackTraceElement[] element = e.getStackTrace();
		stackTraceMessage.append(e.getMessage()).append("\r\n").append("\t");
		stackTraceMessage.append(e.getClass()).append("\r\n");
		for (int i = 0; i < element.length; i++) {
			stackTraceMessage.append("\t\t").append(element[i]).append("\r\n");
		}
		return stackTraceMessage.toString();
	}
}
